package com.hichao.cs.thrift.front;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.server.ServerContext;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TServerEventHandler;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.apache.thrift.transport.TTransportFactory;
import org.springframework.beans.factory.InitializingBean;

import com.hichao.cs.helper.CSHelper;


public class FrontServerFactory implements InitializingBean, CSHelper {
	
	private int port;
	private ServerThread serverThread;
	
	public void setPort(int port){
		this.port = port;
	}
	
	static class FrontServerContext implements ServerContext {
	
	    int connectionId;
	
	    public FrontServerContext(int connectionId) {
	        this.connectionId = connectionId;
	    }
	
	    public int getConnectionId() {
	        return connectionId;
	    }
	
	    public void setConnectionId(int connectionId) {
	        this.connectionId = connectionId;
	    }
	
	}	
	
	static class FrontServerEventHandler implements TServerEventHandler {
	
		private int nextConnectionId = 1;
		
	    public void preServe() {
		        logger.info("TServerEventHandler.preServe - called only once before server starts accepting connections");
	    }
		
	    public ServerContext createContext(TProtocol input, TProtocol output) {
	        //we can create some connection level data which is stored while connection is alive & served
	        FrontServerContext ctx = new FrontServerContext(nextConnectionId++);
	        logger.info("TServerEventHandler.createContext - connection #"+ctx.getConnectionId()+" established");
	        return ctx;
	    }
	
	    public void deleteContext(ServerContext serverContext, TProtocol input, TProtocol output) {
	    	FrontServerContext ctx = (FrontServerContext)serverContext;
	        logger.info("TServerEventHandler.deleteContext - connection #"+ctx.getConnectionId()+" terminated");
	    }
	
	    public void processContext(ServerContext serverContext, TTransport inputTransport, TTransport outputTransport) {
	    	FrontServerContext ctx = (FrontServerContext)serverContext;
	        logger.info("TServerEventHandler.processContext - connection #"+ctx.getConnectionId()+" is ready to process next request");
	    }

	}
	
	class ServerThread extends Thread {
		
		private TServer serverEngine;

		ServerThread(int port) throws Exception {
			try{
				this.enter(port);
			}catch(Exception x){
				x.printStackTrace();
			}
		}

		private void enter(int port) throws TTransportException {
			
			FrontServiceImpl handler = new FrontServiceImpl();
			
			@SuppressWarnings({ "rawtypes", "unchecked" })
			FrontService.Processor processor = new FrontService.Processor(handler);
			TProtocolFactory tProtocolFactory = new TBinaryProtocol.Factory();
			TTransportFactory tTransportFactory = new TFramedTransport.Factory();
			
	        TNonblockingServerSocket tNonblockingServerSocket =
	                new TNonblockingServerSocket(
	                		new TNonblockingServerSocket.NonblockingAbstractServerSocketArgs().port(port));
	        TNonblockingServer.Args tNonblockingServerArgs = new TNonblockingServer.Args(tNonblockingServerSocket);
	        tNonblockingServerArgs.processor(processor);
			tNonblockingServerArgs.protocolFactory(tProtocolFactory);
			tNonblockingServerArgs.transportFactory(tTransportFactory);
			this.serverEngine = new TNonblockingServer(tNonblockingServerArgs);
			
			this.serverEngine.setServerEventHandler(new FrontServerEventHandler());
			logger.info("Starting the server on port " + port + "...");
		}

		@Override
		public void run(){
			try{
				this.serverEngine.serve();
			}catch(Exception x){
				x.printStackTrace();
			}
		}
		
		public void exit(){
			this.serverEngine.stop();
		}
		
	}

	public void close() {
		this.serverThread.exit();
	}
	  
	@Override
	public void afterPropertiesSet() throws Exception {
		try{
			this.serverThread = new ServerThread(this.port);
			serverThread.start();
		} catch (Exception x) {
			if (serverThread != null)
			    logger.info("{}", serverThread.getState());
			x.printStackTrace();
			throw x;
		}
	}

}
